/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Bank;
import java.util.*;

/**
 *
 * @author karakots
 */
public class LendingPolicy {

    private int numLoanApplications;
    private int numLoanApplicationsThisQtr;
    private double numRejectedLTI;
    private double numRejectedPctDown;
    private double numRejectedLoanTooSmall;
    private double numRejectedLoanTooLarge;
    public  boolean loanLength15years;
    public boolean loanLength30years;
    private double numAppsQualified;
    private double numAppsQualifiedThisQtr;

    private int numloansEverOffered;
    private int numLoansOfferedThisQtr;
    private int numLoansPaidOff;
    private int numLoansPaidOffThisQtr;
    private int numLoansEverPreapproved;
    private int numLoansPreapprovedThisQtr;
    private int numLoansEverFunded;
    private int numDefaultsEver;

    private int numLoansCurrentlyPreApproved;
    private int numLoansCurrentlyOnOffer;
    private int numLoanOffersExpired;
    public boolean fixedRateLoans;
    public double monthlyMortgageInterestRate;
    public double maxPaymentToIncome;
    public double mortgagePoints;
    public double minMortgagePctDown;
    public double minLoanSize;                 // smallest loan provided
    public double monthlyRateSpread;
    public double maxLoanSize;

    private double lastQtrNumRejectedLTI;
    private double lastQtrNumRejectedPctDown;
    private double lastQtrNumRejectedLoanTooSmall;
    private double lastQtrNumRejectedLoanTooLarge;
    private double lastQtrNumLoanApps;
    private double thisQtrNumRejectedLTI;
    private double thisQtrNumRejectedPctDown;
    private double thisQtrNumRejectedLoanTooSmall;
    private double thisQtrNumRejectedLoanTooLarge;
    private double thisQtrNumLoanApps;
    private double lastQtrPctNumRejectedLTI;
    private double lastQtrPctNumRejectedPctDown;
    private double lastQtrPctNumRejectedLoanTooSmall;
    private double lastQtrPctNumRejectedLoanTooLarge;
    private double lastQtrPctNumLoanApps;
    private double thisQtrPctNumRejectedLTI;
    private double thisQtrPctNumRejectedPctDown;
    private double thisQtrPctNumRejectedLoanTooSmall;
    private double thisQtrPctNumRejectedLoanTooLarge;
    private double thisQtrPctNumLoanApps;


    private boolean adaptive;
    private double adaptLoanAppsSinceLastAdapt;
    private double adaptMinLoanApps;
    private double adaptMinPctApproved;
    private double adaptNumRejectedLTI;
    private double adaptNumRejectedPctDown;
    private double adaptNumRejectedLoanTooSmall;
    private double adaptNumRejectedLoanTooLarge;
    private double adaptRateOfChangeLoosen;
    private double adaptRateOfChangeTighten;
    private double adaptNumDefaults;
    private double adaptMaxPctDefaults;
    private double adaptEvalsSinceLastTighten;
    private double adaptMinEvalsTightenToLoosen;
    private Bank myBank;


    public void setAdaptive(boolean b)                  {adaptive = b;}
    public boolean getAdaptive()                        {return adaptive;}
    public int getNumLoansCurrentlyOnOffer()            {return numLoansCurrentlyOnOffer;}
    public int getNumLoansCurrentlyPreApproved()        {return numLoansCurrentlyPreApproved;}
    // the loan pipeline
    public int getNumloansEverOffered()                 {return numloansEverOffered;}
    public int getNumLoansOfferedThisQtr()              {return numLoansOfferedThisQtr;}
    public int getNumLoansEverPreapproved()             {return numLoansEverPreapproved;}
    public int getNumLoansPreapprovedThisQtr()          {return numLoansPreapprovedThisQtr;}
    public int getNumLoansPaidOff()                     {return numLoansPaidOff;}
    public int getNumLoansPaidOffThisQtr()              {return numLoansPaidOffThisQtr;}
    public int getNumLoansEverFunded()                  {return numLoansEverFunded;}
    public int getNumDefaultsEver()                     {return numDefaultsEver;}
    public int getNumLoanApplications()                 {return numLoanApplications;}
    public int getNumLoanApplicationsThisQtr()          {return numLoanApplicationsThisQtr;}

    public double pctLoanAppsQualifying() {
        double numApps = getNumLoanApplications();
        if (numApps == 0)
            return 0;
        else
            return numAppsQualified / numApps;
    }

    public double pctLoanAppsQualifyingThisQtr() {
        double numApps = getNumLoanApplicationsThisQtr();
        if (numApps == 0)
            return 0;
        else
            return numAppsQualifiedThisQtr / numApps;
    }

    private enum LoosenState {
        kLTI, kPctDown, kLoanTooSmall, kLoanTooLarge, kNone
    }

    public LendingPolicy(Bank bank) {
        reset();
        adaptive = false;
        myBank = bank;
    }

    public void newQuarter() {
        lastQtrNumLoanApps = thisQtrNumLoanApps;
        lastQtrNumRejectedLTI = thisQtrNumRejectedLTI;
        lastQtrNumRejectedPctDown = thisQtrNumRejectedPctDown;
        lastQtrNumRejectedLoanTooSmall = thisQtrNumRejectedLoanTooSmall;
        lastQtrNumRejectedLoanTooLarge = thisQtrNumRejectedLoanTooLarge;

        lastQtrPctNumRejectedLTI = thisQtrPctNumRejectedLTI;
        lastQtrPctNumRejectedPctDown = thisQtrPctNumRejectedPctDown;
        lastQtrPctNumRejectedLoanTooSmall = thisQtrPctNumRejectedLoanTooSmall;
        lastQtrPctNumRejectedLoanTooLarge = thisQtrPctNumRejectedLoanTooLarge;
        lastQtrPctNumLoanApps = thisQtrPctNumLoanApps;

        if (thisQtrNumLoanApps > 0) {
            thisQtrPctNumRejectedLTI =          100 * thisQtrNumRejectedLTI / thisQtrNumLoanApps;
            thisQtrPctNumRejectedPctDown =      100 * thisQtrNumRejectedPctDown / thisQtrNumLoanApps;
            thisQtrPctNumRejectedLoanTooSmall = 100 * thisQtrNumRejectedLoanTooSmall / thisQtrNumLoanApps;
            thisQtrPctNumRejectedLoanTooLarge = 100 * thisQtrNumRejectedLoanTooLarge / thisQtrNumLoanApps;
        } else {
            thisQtrPctNumRejectedLTI = 0;
            thisQtrPctNumRejectedPctDown = 0;
            thisQtrPctNumRejectedLoanTooSmall = 0;
            thisQtrPctNumRejectedLoanTooLarge = 0;
            thisQtrPctNumLoanApps = 0;
        }

        thisQtrNumRejectedLTI = 0;
        thisQtrNumRejectedPctDown = 0;
        thisQtrNumRejectedLoanTooSmall = 0;
        thisQtrNumRejectedLoanTooLarge = 0;
        thisQtrNumLoanApps = 0;
        numLoansOfferedThisQtr = 0;
        numLoansPreapprovedThisQtr = 0;
        numLoansPaidOffThisQtr = 0;
        numLoanApplicationsThisQtr = 0;
        numAppsQualifiedThisQtr = 0;
    }

    void reset() {
        numLoanApplications = 0;
        numRejectedLTI = 0;
        numRejectedPctDown = 0;
        numRejectedLoanTooSmall = 0;
        numLoansCurrentlyOnOffer = 0;
        numLoanOffersExpired = 0;
        numLoansCurrentlyPreApproved = 0;
        numRejectedLoanTooLarge = 0;
        numLoansEverFunded = 0;
        numloansEverOffered = 0;
        numLoansOfferedThisQtr = 0;
        numLoansEverPreapproved = 0;
        numLoansPreapprovedThisQtr = 0;
        numLoansPaidOff = 0;
        numLoansPaidOffThisQtr = 0;
        numDefaultsEver = 0;
        numLoanApplicationsThisQtr = 0;
        numAppsQualified = 0;
        numAppsQualifiedThisQtr = 0;

        lastQtrNumLoanApps = 0;
        lastQtrNumRejectedLTI = 0;
        lastQtrNumRejectedPctDown = 0;
        lastQtrNumRejectedLoanTooSmall = 0;
        lastQtrNumRejectedLoanTooLarge = 0;
        thisQtrNumRejectedLTI = 0;
        thisQtrNumRejectedPctDown = 0;
        thisQtrNumRejectedLoanTooSmall = 0;
        thisQtrNumRejectedLoanTooLarge = 0;
        thisQtrNumLoanApps = 0;

        lastQtrPctNumRejectedLTI = 0;
        lastQtrPctNumRejectedPctDown = 0;
        lastQtrPctNumRejectedLoanTooSmall = 0;
        lastQtrPctNumRejectedLoanTooLarge = 0;
        lastQtrPctNumLoanApps = 0;
        thisQtrPctNumRejectedLTI = 0;
        thisQtrPctNumRejectedPctDown = 0;
        thisQtrPctNumRejectedLoanTooSmall = 0;
        thisQtrPctNumRejectedLoanTooLarge = 0;
        thisQtrPctNumLoanApps = 0;


        resetAdaptCounters();
        adaptMinLoanApps = 100;
        adaptMinPctApproved = 0.05;
        adaptRateOfChangeLoosen = 0.05;
        adaptRateOfChangeTighten = 0.1;
        adaptMaxPctDefaults = 0.05;
        adaptMinEvalsTightenToLoosen = 5;
        adaptEvalsSinceLastTighten = adaptMinEvalsTightenToLoosen + 1;
    }


    // lending policy transactions -- shoudl probably convert to anenum like BankBooks
    public void preApproveLoan() {
        numLoansCurrentlyPreApproved++;
        numLoansCurrentlyOnOffer--;
        numLoansEverPreapproved++;
        numLoansPreapprovedThisQtr++;
    }

    public void appQualified() {
        numAppsQualified++;
        numAppsQualifiedThisQtr++;
    }

    public void offerExpired() {
        numLoansCurrentlyOnOffer--;
        numLoanOffersExpired++;
    }

    public void preapprovedExpired() {
        numLoansCurrentlyPreApproved--;
    }

    public void addOffer() {
        numLoansCurrentlyOnOffer++;
        numloansEverOffered++;
        numLoansOfferedThisQtr++;
    }

    public void loanFunded() {
        numLoansCurrentlyPreApproved--;
        numLoansEverFunded++;
    }

    public void mortgagePaidOff() {
        numLoansPaidOff++;
        numLoansPaidOffThisQtr++;
    }

    public void loanDefault() {
        adaptNumDefaults++;
        numDefaultsEver++;
    }
    // end transactions

    public double getPctRejectedLTI() {
        if (numLoanApplications > 0)
            return 100 * numRejectedLTI / numLoanApplications;
        else
            return 0;
    }
    public double getPctRejectedPctDown() {
        if (numLoanApplications > 0)
            return 100 * numRejectedPctDown / numLoanApplications;
        else
            return 0;
    }
    public double getPctRejectedLoanTooSmall() {
        if (numLoanApplications > 0)
            return 100 * numRejectedLoanTooSmall / numLoanApplications;
        else
            return 0;
    }
    public double getPctRejectedLoanTooLarge() {
        if (numLoanApplications > 0)
            return 100 * numRejectedLoanTooLarge / numLoanApplications;
        else
            return 0;
    }
    public double getQtrChgPctRejectedLTI() {
        return thisQtrPctNumRejectedLTI - lastQtrPctNumRejectedLTI;
    }
    public double getQtrChgPctRejectedPctDown() {
        return thisQtrPctNumRejectedPctDown - lastQtrPctNumRejectedPctDown;
    }
    public double getQtrChgPctRejectedLoanTooSmall() {
        return thisQtrPctNumRejectedLoanTooSmall - lastQtrPctNumRejectedLoanTooSmall;
    }
    public double getQtrChgPctRejectedLoanTooLarge() {
        return thisQtrPctNumRejectedLoanTooLarge - lastQtrPctNumRejectedLoanTooLarge;
    }
    public double getLastQtrNumLoanApps()               {return lastQtrNumLoanApps;}

    public void newApplication() {
        numLoanApplications++;
        thisQtrNumLoanApps++;
        adaptLoanAppsSinceLastAdapt++;
        numLoanApplicationsThisQtr++;
    }

    public void rejectLTI() {
        numRejectedLTI++;
        thisQtrNumRejectedLTI++;
        adaptNumRejectedLTI++;
    }

    public void rejectPctDown() {
        numRejectedPctDown++;
        thisQtrNumRejectedPctDown++;
        adaptNumRejectedPctDown++;
    }

    public void rejectLoanTooSmall() {
        numRejectedLoanTooSmall++;
        thisQtrNumRejectedLoanTooSmall++;
        adaptNumRejectedLoanTooSmall++;
    }

    public void rejectLoanTooLarge() {
        numRejectedLoanTooLarge++;
        thisQtrNumRejectedLoanTooLarge++;
        adaptNumRejectedLoanTooLarge++;
    }

    public  void adapt() {
        adaptEvalsSinceLastTighten++;
        // don't try to adapt to often
        if (adaptLoanAppsSinceLastAdapt < adaptMinLoanApps)
            return;
        // if too many defaults
        else if ((numLoansEverFunded > 0) && (adaptNumDefaults/numLoansEverFunded) > adaptMaxPctDefaults) {
            tightenPolicy();
        }
        else if (adaptEvalsSinceLastTighten >= adaptMinEvalsTightenToLoosen) {
            // if too few loans approved
            double pctLoansApproved = numLoansCurrentlyPreApproved / numLoanApplications;
            if (pctLoansApproved < adaptMinPctApproved) {
                loosenPolicy();
            }
        }
        resetAdaptCounters();
    }

    private String makePctString(double valIn) {
            return String.format("%2.2f",valIn * 100);
    }


    private void tightenPolicy() {
        myBank.setHasAdapted(true);
        double oldMaxPTI = maxPaymentToIncome;
        double oldMinDownPct = minMortgagePctDown;
        // decrease max payment to income or increase down payment
        maxPaymentToIncome *= (1 - adaptRateOfChangeTighten);
        if (minMortgagePctDown < 0.05)
            minMortgagePctDown = 0.05;
        else
            minMortgagePctDown *= (1 + adaptRateOfChangeTighten);
        adaptEvalsSinceLastTighten = 0;

        if (myBank.getLogData()) {
            myBank.policyRecord.add(myBank.getDate() + 
                   " Tightening lending by decreasing max payment to income from " +
                   makePctString(oldMaxPTI) + "% to " + makePctString(maxPaymentToIncome) +
                   "% and increasing min percent down from " + makePctString(oldMinDownPct) + "% to " +
                   makePctString(minMortgagePctDown) + "%");
        }
    }

    private void resetAdaptCounters() {
        adaptLoanAppsSinceLastAdapt = 0;
        adaptNumRejectedLTI = 0;
        adaptNumRejectedPctDown = 0;
        adaptNumRejectedLoanTooSmall = 0;
        adaptNumRejectedLoanTooLarge = 0;
        adaptNumDefaults = 0;
    }

    private void loosenPolicy() {
       // get highest probability for reason for refusing loans
       myBank.setHasAdapted(true);
       LoosenState ls = LoosenState.kNone;
       double maxRejected = 0;

       if (adaptNumRejectedLTI > maxRejected) {
            ls = LoosenState.kLTI;
               maxRejected = adaptNumRejectedLTI;
       }
       if (adaptNumRejectedPctDown > maxRejected) {
           ls = LoosenState.kPctDown;
           maxRejected = adaptNumRejectedPctDown;
       }
       if (adaptNumRejectedLoanTooSmall > maxRejected) {
           ls = LoosenState.kLoanTooSmall;
           maxRejected = adaptNumRejectedLoanTooSmall;
       }
       if (adaptNumRejectedLoanTooLarge > maxRejected) {
           ls = LoosenState.kLoanTooLarge;
           maxRejected = adaptNumRejectedLoanTooLarge;
       }

       // relax this constraint
       switch(ls) {
           case kLTI: {
               if (maxPaymentToIncome < 0.6) {
                   double oldVal = maxPaymentToIncome;
                    maxPaymentToIncome *= (1+adaptRateOfChangeLoosen);
                    if (myBank.getLogData())
                        myBank.policyRecord.add(myBank.getDate() +
                            " Loosening lending by increasing max payment to income from " +
                            makePctString(oldVal) + "% to " + makePctString(maxPaymentToIncome) + "%");
               }
               break;
           }
           case kPctDown: {
               double oldVal = minMortgagePctDown;
               minMortgagePctDown *= (1-adaptRateOfChangeLoosen);
               if (myBank.getLogData())
                    myBank.policyRecord.add(myBank.getDate() +
                        " Loosening lending by decreasing min percent down from " +
                        makePctString(oldVal) + "% to " + makePctString(minMortgagePctDown) + "%");
               break;
           }
           case kLoanTooSmall: {
               double oldVal = minLoanSize;
               minLoanSize *= (1-adaptRateOfChangeLoosen);
               if (myBank.getLogData())
                    myBank.policyRecord.add(myBank.getDate() +
                        " Loosening lending by decreasing min loan size from " +
                        (oldVal /1000) + "$K to " + minLoanSize + "$K");
              break;
           }
           case kLoanTooLarge: {
               double oldVal = maxLoanSize;
               maxLoanSize *= (1+adaptRateOfChangeLoosen);
                if (myBank.getLogData())
                    myBank.policyRecord.add(myBank.getDate() +
                        " Loosening lending by increasing max loan size from " +
                        (oldVal /1000) + "$K to " + maxLoanSize + "$K");
              break;
           }
       }

       // also consider lowering interest rate
   }
}
